//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#include "precomp.h"
#ifdef RIL_LAST_ERROR
#include "ccoreutl.h"
#endif

#ifdef UNDER_CE
#include <pegdser.h>
#endif // UNDER_CE

#ifndef DUAL_PORT_SERIAL
#include "muxapi.h"
#endif

/* For interface with FFUART on Zylonite */
#include <serhw.h>
#include <16550.h>

// Defines the maximum number of attempts of sending "AT<cr>" after a command timeout.
// Reconmended value is 1 (one attempt). If set to 0 the module is shut down immediately
// after a command timeout. Higher values can be used for debug purposes.
#define MAX_AT_ATTEMPTS_AFTER_TIMEOUT         1

#ifdef RIL_FAKECSQ
volatile BOOL g_fFakeSignalStrength = FALSE;
#endif

// Initialize radio off to true, since this is how we boot up.
BOOL g_bRadioOff = TRUE;
#ifdef RIL_LAST_ERROR
extern DWORD g_dwLastError;
#endif

extern BOOL g_fGPRSActive;
extern BOOL g_fReleaseHeldCall;
extern RHA_FUNCTION_LIST RHAFunctionList;

//
// Virtual serial device handle ctor
//
CComHandle::CComHandle(COM_PORT_TYPE PortType) :
m_hDownstream(INVALID_HANDLE_VALUE),
m_hDataModeInterrupted(NULL),    
m_hCComHandleDestroyEvent(NULL),
m_hCommandModeEvent(NULL),
m_hComCancelEvent(NULL),
m_hComInitEvent(NULL),
m_hExternalCancelEvent(NULL),
m_fCheckCmdMode(FALSE),
m_pBuffer(NULL),
m_dwDownstreamBaudRate(RIL_FFUART_BAUDRATE),
m_dwStatQuantum(500),
m_pOwner(NULL),
m_fInited(FALSE),
m_fDataMode(FALSE),
m_fCancelledDial(FALSE),
m_PortType(INVALID_PORT),
m_dwLastCmdSent(0),
m_dwLastDataSent(0),
m_fCommandOngoing(FALSE)
{
    // TBD_FUNCTION(CComHandle::CComHandle);
    DWORD dwTemp;

    m_PortType = PortType;

    InitializeCriticalSection(&m_csAbortCommand);
    InitializeCriticalSection(&m_csDataMode);
    InitializeCriticalSection(&m_csOwner);
    InitializeCriticalSection(&m_csStats);
    InitializeCriticalSection(&m_csPortHandle);

    // Read the baud rate for the downstream modem
    //if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("BaudRate"), &dwTemp))
    //{
    //    m_dwDownstreamBaudRate = dwTemp;
    //}

    // Read the data mode interruption time
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DataModeInteruptionQuantum"), &dwTemp))
    {
        m_dwStatQuantum = dwTemp;
    }

    RILRetailTrace((TEXT("CComHandle::CComHandle(Port %d) \n"), PortType));    
    RILRetailTrace((TEXT("m_dwDownstreamBaudRate = %d \n"), m_dwDownstreamBaudRate));    
    RILRetailTrace((TEXT("m_dwStatQuantum = %d \n"), m_dwStatQuantum));    
    
    
    m_hCComHandleDestroyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // Intialize read and write data bickets to 0
    memset(m_rgdwReadStatBits,  0x00, STAT_BUCKETS * sizeof(DWORD));
    memset(m_rgdwWriteStatBits, 0x00, STAT_BUCKETS * sizeof(DWORD));

    // Set the initial timestamps for com activity stats
    m_dwReadStatTimestamp = GetTickCount();
    m_dwWriteStatTimestamp = m_dwReadStatTimestamp;
}


//
// Virtual serial device handle dtor
//
CComHandle::~CComHandle()
{
    // TBD_FUNCTION(CComHandle::~CComHandle);

    if (m_hCComHandleDestroyEvent) {
        SetEvent(m_hCComHandleDestroyEvent);
    }

    CloseDownstreamPort();

    if (m_hCommandModeEvent)
    {
        CloseHandle(m_hCommandModeEvent);
        m_hCommandModeEvent = NULL;
    }

    if (m_hComCancelEvent)
    {
        CloseHandle(m_hComCancelEvent);
        m_hComCancelEvent = NULL;
    }

    if (m_hComInitEvent)
    {
        CloseHandle(m_hComInitEvent);
        m_hComInitEvent = NULL;
    }
        

    if (m_hDataModeInterrupted)
    {
        CloseHandle(m_hDataModeInterrupted);
        m_hDataModeInterrupted = NULL;
    }

    delete m_pBuffer;
    m_pBuffer = NULL;

    DeleteCriticalSection(&m_csAbortCommand);
    DeleteCriticalSection(&m_csDataMode);
    DeleteCriticalSection(&m_csOwner);
    DeleteCriticalSection(&m_csStats);
    DeleteCriticalSection(&m_csPortHandle);
    m_fInited = FALSE;
}


//
// Virtual serial device handle initialization
//
BOOL CComHandle::Init()
{
    // TBD_FUNCTION(CComHandle::Init);
    if (m_fInited)
    {
        goto Error;
    }

    // Create required events
    m_hCommandModeEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (!m_hCommandModeEvent)
    {
        goto Error;
    }
    m_hComCancelEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (!m_hComCancelEvent)
    {
        goto Error;
    }
    m_hComInitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hComInitEvent)
    {
        goto Error;
    }

    m_hDataModeInterrupted = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hDataModeInterrupted)
    {
        goto Error;
    }

    // Create backup buffer
    m_pBuffer = new CBuffer;
    if (!m_pBuffer)
    {
        goto Error;
    }

    m_fInited = TRUE;

    Error:
    return m_fInited;
}


//
// Open downstream port
//
BOOL CComHandle::OpenDownstreamPort()
{
    // TBD_FUNCTION(CComHandle::OpenDownstreamPort);

#ifdef UNDER_CE
    DWORD dwFlags = 0;
#else   // UNDER_CE
    DWORD dwFlags = FILE_FLAG_OVERLAPPED;
#endif // UNDER_CE

    TCHAR* tszComName = NULL;
    DWORD dwTimeout;
    DWORD nAttempts = 0;
    DWORD nMaxAttempts;
    BOOL fRet = FALSE;

    SYNCBLOCK(m_csPortHandle);

    if (INVALID_HANDLE_VALUE == m_hDownstream)
    {
        // Get the COM port name
        tszComName = g_ComManager.GetInternalPortName(m_PortType);
        if (!tszComName)
        {
          TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CComHandle::OpenDownstreamPort : COM port name not found in registry"));
          goto Error;
        }

        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CComHandle::OpenDownstreamPort : Connecting to COM port: %s"), tszComName);

        // Get the max number of times we should try to open the COM port
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("MaxOpenAttempts"), &nMaxAttempts))
        {
            nMaxAttempts = 3;
        }

        // Get the amount of time we should sleep between attempts
        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("OpenTimeout"), &dwTimeout))
        {
            dwTimeout = 1000;
        }

        // Open the COM port
        while (1)
        {
            // Try to open the COM port
            m_hDownstream = CreateFile(tszComName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFlags, NULL);

            // If we succeeded or exhausted the number of attempts, quit looping
            nAttempts++;
            if ((m_hDownstream != INVALID_HANDLE_VALUE) || (nAttempts >= nMaxAttempts))
            {
                break;
            }

            // Sleep the required amount of time and try again
            Sleep(dwTimeout);
        }

        if (INVALID_HANDLE_VALUE == m_hDownstream)
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::OpenDownstreamPort : Couldn't open downstream port"));
            goto Error;
        }

        if (!InitCommState(NULL))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::OpenDownstreamPort : Couldn't Init comm state"));
            goto Error;
        }

        if (!ResetEvent(m_hComCancelEvent))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::OpenDownstreamPort : Couldn't reset cancel event"));
            goto Error;
        }

        if (!SetEvent(m_hComInitEvent))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::OpenDownstreamPort : Couldn't set init event"));
            goto Error;
        }
    }

    fRet = TRUE;

    Error:
    if (!fRet)
    {
        if (INVALID_HANDLE_VALUE != m_hDownstream)
        {
            (void)CloseHandle(m_hDownstream);
            m_hDownstream = INVALID_HANDLE_VALUE;
        }
    }
    return fRet;
}


//
// Close downstream port
//
BOOL CComHandle::CloseDownstreamPort()
{
    // TBD_FUNCTION(CComHandle::CloseDownstreamPort);

    SYNCBLOCK(m_csPortHandle);

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream)) {
      return FALSE;
    }

    // Make sure that data port's has left the data mode
    if (g_ComManager.IsDataPort(m_PortType)) {
      if (RHAFunctionList.pfnRHA_IsRadioPwrOn()) 
      {
         /* Exit Data Mode */
         TerminateDataMode();
      }
    }

    // Abort possible running command
    AbortCommand();

    // Release all the waiting operations
    ResetEvent(m_hComInitEvent);
    SetEvent(m_hComCancelEvent);

    // Close the downstream port
    CloseHandle(m_hDownstream);
    m_hDownstream = INVALID_HANDLE_VALUE;

    return TRUE;
}


//
// Wait until a valid downstream port is available or we're cancelled
//
BOOL CComHandle::WaitForDownstreamPort()
{
    HANDLE  hEvent[3] = { m_hComInitEvent, m_hCComHandleDestroyEvent, m_hExternalCancelEvent };
    if (m_hCComHandleDestroyEvent && m_hExternalCancelEvent) {
        return (WaitForMultipleObjects(3, hEvent, FALSE, INFINITE) == WAIT_OBJECT_0);
    }
    return FALSE;
}


//
// Serial IO-control
//
BOOL CComHandle::IOControl(const DWORD dwCode, const BYTE* const pBufIn, const DWORD dwLenIn, BYTE* const pBufOut,
                           const DWORD dwLenOut, DWORD& rdwActualOut, BOOL fLockSignals) const
{
    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream)) {
        return FALSE;
    }

    // Don't let the outside users change DTR because this throws module out of data mode
    if (fLockSignals && ((IOCTL_SERIAL_SET_DTR == dwCode) || (IOCTL_SERIAL_CLR_DTR == dwCode))) {
      return TRUE;
    }

    // Don't let the outside users reset the EV_DSR and EV_RLSD flags in the wait mask if we're in gprs mode
    // because the CheckCmdModeThread needs them to detect if data mode has been left
    if (m_fCheckCmdMode && (IOCTL_SERIAL_SET_WAIT_MASK == dwCode)) {
      *(PDWORD)pBufIn |= CMD_MODE_MASK;
      //return TRUE;
    }

    // Finally call the downstream port
    return DeviceIoControl(m_hDownstream, dwCode, (void*)pBufIn, dwLenIn, pBufOut, dwLenOut, &rdwActualOut, NULL);
}


//
// Thread program checking for command mode during GPRS connections
//
DWORD WINAPI CComHandle::CheckCmdModeThread(LPVOID lpParam) {
    //TBD_FUNCTION(CComHandle::CheckCmdModeThread);

    CComHandle*  pPort = (CComHandle*)lpParam;
    DWORD  dwModemStatus = 0;
    DWORD  dwRingNums = 0;
    DWORD  dwFstRingTime = 0;
    DWORD  dwSndRingTime = 0;    
    DWORD  dwEventData = 0;
	DWORD  dwMask = 0;
    BOOL bExitDataModeFlag = FALSE;

    RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : Entered ....\r\n")));
    //ResetEvent(pPort->m_hRingIndicateEvent);

    while (pPort->FDataMode()) {

      if (pPort->VirtWaitCommEvent(&dwMask)) {
        if (pPort->FDataMode()) {
          if (!pPort->VirtGetCommModemStatus(&dwMask)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::CheckCmdModeThread : VirtGetCommModemStatus failed"));
            RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : VirtGetCommModemStatus failed\r\n")));
            return -1;
			}
			if ((dwMask & CMD_MODE_STATUS) != CMD_MODE_STATUS) {
            CRilHandle *pHandle;

            // Module has left data mode
            RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : Detected command mode\r\n")));
            pPort->ExitDataMode();

            // We also had to inform the OS, that a connection had been lost. The OS will call the RIL to find
            // out, which connection is lost, so we don't need to send any additional information here.
            pHandle = g_ComManager.GetRILDevice();
            if (pHandle != NULL)
                pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);

      		}  // End if VirtGetCommModemStatus
          } // End if FDataMode
       } //End if VirtWaitCommEvent
    } //End while

    pPort->m_fCheckCmdMode = FALSE;

    RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : Left\r\n")));

    return 0;
}


//
// NOTE: multiple calls to this without corresponding calls to ExitDataMode() are OK.
//
BOOL CComHandle::EnterDataMode(const LPCSTR szData, const UINT cbData)
{
    DWORD dwMask = 0;
    TBD_FUNCTION(CComHandle::EnterDataMode);

    if (!g_ComManager.IsDataPort(m_PortType)) {
        return FALSE;
    } else {
        SYNCBLOCK(m_csDataMode);

        if (cbData && !m_pBuffer->Append(szData, cbData)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : cbData and Append failed"));
            return FALSE;
        }
        TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CComHandle::EnterDataMode : Entered data mode, stored %d bytes in the buffer"), cbData);

        // Make sure to block anyone who waits for command mode
        if (!ResetEvent(m_hCommandModeEvent)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : ResetEvent failed"));
            return FALSE;
        }

        // Start the thread checking for command mode
        if (!VirtSetCommMask(0)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : VirtSetCommMask failed"));
            return FALSE;
        }

        if (!VirtSetCommMask(CMD_MODE_MASK)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : VirtSetCommMask failed"));
            return FALSE;
        }

        if (!VirtGetCommMask(&dwMask)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : VirtGetCommMask failed"));
            return FALSE;
        }
        RILRetailTrace((TEXT("RilDrv: CComHandle::EnterDataMode : dwMask = %x\r\n"), dwMask));

        // Switch to data mode and store the supplied data in the backup buffer
        m_fDataMode = TRUE;


#ifdef  RIL_NO_MODE_DETECT_THREAD
		/* If there is no DSR and DCD pin connection of FFUART for RIL driver *
		/* It means that we can not detect data mode and command mode by DSR and DCD pin status */
        m_fCheckCmdMode = FALSE;
        RILRetailTrace((TEXT("RilDrv: CComHandle::EnterDataMode : No  CheckCmdModeThread thread. \r\n")));
		
#else
        m_fCheckCmdMode = TRUE;

        HANDLE hThread = CreateThread(0, 0, CheckCmdModeThread, this, 0, 0);
        if (!hThread) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : CreateThread failed"));
            return FALSE;
        }
        CloseHandle(hThread);
#endif

        // Enable the internal virtual application data port
        if (!g_ComManager.UnlockPortDevice(m_PortType)) {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::EnterDataMode : Failed to enable the internal virtual application data port"));
            return FALSE;
        }
    }

    return TRUE;
}


//
//
//
BOOL CComHandle::ExitDataMode()
{
    TBD_FUNCTION(CComHandle::ExitDataMode);

    SYNCBLOCK(m_csDataMode);

    // If data mode already left, do nothing
    if (!m_fDataMode) {
        return TRUE;
    }

    BOOL fRet = TRUE;

    // Disable the internal virtual application data port
    if (!g_ComManager.LockPortDevice(m_PortType)) {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::ExitDataMode : Failed to disable the internal virtual application data port"));
        fRet = FALSE;
    }

    // Make sure no handle owns the VSP anymore
    (void)ClearOwner(m_pOwner);

    // Clear the backup data buffer
    delete[] m_pBuffer->GiveUpData();

    // We're not in data mode anymore
    m_fDataMode = FALSE;

    // Re-initialize COM port for RIL use
    if (!InitComPortForRIL(NULL, NULL)) {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::ExitDataMode : InitComPortForRIL failed"));
        fRet = FALSE;
    }

    // Make sure to release anyone waiting for command mode
    if (!SetEvent(m_hCommandModeEvent)) {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CComHandle::ExitDataMode : SetEvent failed"));
        fRet = FALSE;
    }

    return fRet;
}


//
// Manual termination of data mode
//
BOOL CComHandle::TerminateDataMode()
{
    BOOL bRet = TRUE;
    TBD_FUNCTION(CComHandle::TerminateDataMode);

	// If data mode already left, do nothing
	if (!m_fDataMode) {
		return TRUE;
	}

	// Set DTR and clear DTR to leave data mode
	if (!VirtEscapeCommFunction(SETDTR))
	{
		RILRetailTrace((TEXT("RilDrv: TerminateDataMode Failed in VirtEscapeCommFunction(SETDTR) \r\n")));
		bRet = FALSE;
	}

	Sleep(200);

	if (!VirtEscapeCommFunction(CLRDTR))
	{
		RILRetailTrace((TEXT("RilDrv: TerminateDataMode Failed in VirtEscapeCommFunction(CLRDTR) \r\n")));
		bRet = FALSE;
	}

	if (!ExitDataMode())
	{
		RILRetailTrace((TEXT("RilDrv: TerminateDataMode Failed in ExitDataMode() \r\n")));
		bRet = FALSE;
	} 

	RILRetailTrace((TEXT("RilDrv: TerminateDataMode ExitDataMode() Completed\r\n")));


    CRilHandle *pHandle;

	pHandle = g_ComManager.GetRILDevice();
    if (pHandle != NULL)
        pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_LOCAL);

    return bRet;
}

//
//
//
BOOL CComHandle::WaitForCommandMode(DWORD dwTimeOut) const
{
    HANDLE rgEvents[2] = { m_hCommandModeEvent, m_hComCancelEvent };

    if (WAIT_OBJECT_0 != WaitForMultipleObjects(2, rgEvents, FALSE, dwTimeOut)) {
        return FALSE;
    }

    return TRUE;
}


//
//
//
BOOL CComHandle::AbortCommand()
{
    SYNCBLOCK(m_csDataMode);
    BOOL fRet = FALSE;
    if (!FDataMode() && RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
      SYNCBLOCK(m_csAbortCommand);
      DWORD i;
      // First try for 1 second if we might have a fast command we don't need to abort
      for (i = 0; (i < 10) && m_fCommandOngoing; i++) {
        Sleep(100);
      }
      if (m_fCommandOngoing) {
        RILRetailTrace((TEXT("RilDrv: Aborting command (%s)\r\n"), g_ComManager.GetDebugName(m_PortType)));
        if (VirtWrite(ABORT_COMMAND, ABORT_COMMAND_SIZE, i)) {
          if (i == ABORT_COMMAND_SIZE) {
            for (i = 0; i < 1500; i++) {
              Sleep(100);
              if (!m_fCommandOngoing) {
                Sleep(100);
                fRet = TRUE;
                break;
              }
            }
          }
        }
      }
    }
    return fRet;
}


//
//
//
void CComHandle::UpdateComStatData(const BOOL fRead, const DWORD dwBytes)
{
    // TBD_FUNCTION(CComHandle::UpdateComStatData);
    SYNCBLOCK(m_csStats);

    UINT nBucketsToShift;
    DWORD* pdwBuckets = (fRead ? m_rgdwReadStatBits : m_rgdwWriteStatBits);
    DWORD* pdwLastTime = (fRead ? &m_dwReadStatTimestamp : &m_dwWriteStatTimestamp);
    DWORD dwCurrentTime = GetTickCount();
    BOOL fRet = FALSE;

    if (dwCurrentTime != *pdwLastTime)
    {
        // Determine which bucket the new data goes into
        nBucketsToShift = (dwCurrentTime - *pdwLastTime) / m_dwStatQuantum;

        if (STAT_BUCKETS < nBucketsToShift)
        {
            nBucketsToShift = STAT_BUCKETS;
        }

        if (nBucketsToShift)
        {
            // Shift the data in buckets, if needed
            if (STAT_BUCKETS > nBucketsToShift)
            {
                memmove(pdwBuckets + nBucketsToShift, pdwBuckets, (STAT_BUCKETS - nBucketsToShift) * sizeof(DWORD));
            }

            // Clear out all the new buckets upfront
            memset(pdwBuckets, 0x00, nBucketsToShift * sizeof(DWORD));
        }

        // Place the new data into the first bucket
        *pdwBuckets += (dwBytes * 8);

        // Update the timestamp
        *pdwLastTime = dwCurrentTime;
    }
}


//
//
//
void CComHandle::CalculateComStats(DWORD& rdwReadBitsPerSec, DWORD& rdwWrittenBitsPerSec)
{
    // TBD_FUNCTION(CComHandle::CalculateComStats);
    SYNCBLOCK(m_csStats);

    UINT i;
    DWORD dwReadBitsPerQuantum = 0;
    DWORD dwWrittenBitsPerQuantum = 0;
    HANDLE hThread = GetCurrentThread();
    DWORD dwOldPri;

    // Switch to the highest priority
    dwOldPri = GetThreadPriority(hThread);
    (void)SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);

    // Update the read and write statistics
    UpdateComStatData(TRUE, 0);
    UpdateComStatData(FALSE, 0);

    // Calculate weighted averages of data in read and write buckets (in bits per m_dwStatQuantum msec)
    for (i = 0; i < STAT_BUCKETS; i++)
    {
        dwReadBitsPerQuantum    += m_rgdwReadStatBits[i]  * (STAT_BUCKETS - i) / STAT_BUCKET_WEIGHTS;
        dwWrittenBitsPerQuantum += m_rgdwWriteStatBits[i] * (STAT_BUCKETS - i) / STAT_BUCKET_WEIGHTS;
    }

    // Convert the averages to bits per sec
    rdwReadBitsPerSec    = dwReadBitsPerQuantum    * 1000 / m_dwStatQuantum;
    rdwWrittenBitsPerSec = dwWrittenBitsPerQuantum * 1000 / m_dwStatQuantum;

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CComHandle::CalculateComStats : Com statistics: read - %d bits/sec, write - %d bits/sec"),
               rdwReadBitsPerSec, rdwWrittenBitsPerSec);

    // Switch back to the old priority
    (void)SetThreadPriority(hThread, dwOldPri);
}

//
//
//
DWORD CComHandle::CalculateCmdModeTime()
{
    // FUNCTION_TRACE(CComHandle::CalculateCmdModeTime);
    //DEBUGCHK(0 != m_dwDownstreamBaudRate);

    DWORD dwReadBitsPerSec;
    DWORD dwWriteBitsPerSec;
    DWORD dwBitsPerSecOverAir;
    DWORD dwBitsPerQuantumOverAir;
    DWORD dwTimeToSendBitsToRadio;

    // Calculate average read and write throughput
    CalculateComStats(dwReadBitsPerSec, dwWriteBitsPerSec);
    dwBitsPerSecOverAir = dwReadBitsPerSec + dwWriteBitsPerSec;

    // Calculate how many bits go over the air in m_dwStatQuantum msec, given the average throughput
    //    we just calculated
    dwBitsPerQuantumOverAir = dwBitsPerSecOverAir * m_dwStatQuantum / 1000;

    // Calculate the time we need to send these bits to the radio
    dwTimeToSendBitsToRadio = dwBitsPerQuantumOverAir * 1000 / m_dwDownstreamBaudRate;

    // Calculate the time difference (i.e. the time during which the radio is idle while the bits are sent
    //    over the air)
    return m_dwStatQuantum - dwTimeToSendBitsToRadio;
}

//
//
//
BOOL CComHandle::FEnoughTimeToSendCmd(CCommand* pCmd, DWORD dwTimeLeft) const
{
    // FUNCTION_TRACE(CComHandle::FEnoughTimeToSendCmd);
    //DEBUGCHK(NULL != pCmd);

    BOOL fRet = (pCmd->GetExecTime() <= dwTimeLeft);
    if (!fRet)
    {
        // This command will take too long to execute -- age the command
        pCmd->Age();
    }
    return fRet;
}

DWORD WINAPI WCETimeoutThread(LPVOID lpParameter)
{
    // FUNCTION_TRACE(WCETimeoutThread);
    WCETIMEOUT_THREAD_DATA* pParams = (WCETIMEOUT_THREAD_DATA*)lpParameter;

    if (WAIT_TIMEOUT == WaitForSingleObject(pParams->hCancelEvent, pParams->dwTimeout))
    {
        // We timed out -- cancel WaitCommEvent() in progress
        RILRetailTrace((TEXT("RILDrv : t : WCETimeoutThread : Timed-out - Cancelling WaitCommEvent()\r\n")));
        (void)SetCommMask(pParams->hPort, DEFAULT_COM_MASK);
    }
    return 0;
}

//
//
//
BOOL CComHandle::WaitCommEventWithTimeout(DWORD dwTimeout, DWORD& rdwMask)
{
    // FUNCTION_TRACE(CComHandle::WaitCommEventWithTimeout);
    WCETIMEOUT_THREAD_DATA params;
    DWORD dwThreadID;
    HANDLE hThread = NULL;
    BOOL fRet = FALSE;

    rdwMask = 0;

    params.dwTimeout = dwTimeout;
    params.hPort = m_hDownstream;
    params.hCancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!params.hCancelEvent)
    {
        RILRetailTrace((TEXT("RILDrv : E : CComHandle::WaitCommEventWithTimeout : CreateEvent failed\r\n")));
        goto Error;
    }

    hThread = CreateThread(NULL, 0, WCETimeoutThread, (LPVOID)&params, 0, &dwThreadID);
    if (!hThread)
    {
        RILRetailTrace((TEXT("RILDrv : E : CComHandle::WaitCommEventWithTimeout : CreateThread failed\r\n")));
        goto Error;
    }

    if (!DOWNST_WAITCOMMEVENT(m_hDownstream, &rdwMask))
    {
        RILRetailTrace((TEXT("RILDrv : E : CComHandle::WaitCommEventWithTimeout : DOWNST_WAITCOMMEVENT failed\r\n")));
        goto Error;
    }
    fRet = !!rdwMask;

    Error:
    (void)SetEvent(params.hCancelEvent);
    (void)CloseHandle(hThread);
    (void)CloseHandle(params.hCancelEvent);
    return fRet;
}

//
//
//
BOOL CComHandle::GetDTRDropResponse(CRilHandle* const pRilDevice)
{
    // FUNCTION_TRACE(CComHandle::GetDTRDropResponse);
/*
    char szData[1024];
    DWORD dwRead;
    CBuffer bufTemp;
    LPSTR szTotal;
    LPCSTR szRemainder = NULL;
    BYTE* pbRemainder = NULL;
    UINT cbTotal;
    UINT cbDataBeforeOK;
    UINT cbDataAfterOK;
    DWORD dwErrors;
*/
    DWORD dwNums = 0;
    DWORD dwMaxNums = 5; 
    DWORD dwStatus = 0;
    BOOL fRet = FALSE;
    BOOL fWaitCommEvent = FALSE;
    
    while (dwNums < dwMaxNums)
    {
        RILRetailTrace((TEXT("RILDrv : t : CComHandle::GetDTRDropResponse : start to wait DSR off\r\n")));

        if (GetCommModemStatus(m_hDownstream, &dwStatus))
        {
            if (!(dwStatus & MS_DSR_ON))
            {
                // DSR is off, which means that we're in command mode
                RILRetailTrace((TEXT("RILDrv : i : CComHandle::GetDTRDropResponse : Detected cmd mode due to loss of DSR\r\n")));
                return TRUE;
            }
        }

        Sleep(1000);

        dwNums++;
     }

    return FALSE;

        //if (!fWaitCommEvent)
        //{
        //    RILRetailTrace((TEXT("RILDrv : E : CComHandle::GetDTRDropResponse : WaitCommEventWithTimeout failed\r\n")));
        //    goto Error;
        //}

        
        /*
        RILRetailTrace((TEXT("RILDrv : t : CComHandle::GetDTRDropResponse : WaitCommEventWithTimeout returned, dwMask=0x%x\r\n"),dwMask));
        if (dwMask & EV_ERR)
        {
            // Error is detected on the downstream port
            if (ClearCommError(m_hDownstream, &dwErrors, NULL))
            {
                RILRetailTrace((TEXT("RILDrv : E : CComHandle::GetDTRDropResponse : Detected comm error: %x\r\n"), dwErrors));
            }
        }

        if (dwMask & EV_BREAK)
        {
            // Break is detected on the downstream port
            if (ClearCommBreak(m_hDownstream))
            {
                RILRetailTrace((TEXT("RILDrv : E : CComHandle::GetDTRDropResponse : Detected comm break\r\n")));
            }
        }

        if (dwMask & EV_RXCHAR)
        {
            while (1)
            {
                // Get up to 1K of data
                if (!DOWNST_READFILE(m_hDownstream, szData, 1024, &dwRead))
                {
                    goto Error;
                }

                if (!dwRead)
                {
                    break;
                }

                RILRetailTrace((TEXT("RILDrv : t : CComHandle::GetDTRDropResponse : Read bytes(3): %s\r\n"), TString(PrintableString(szData, dwRead))));

                // Append what we got to the temporary buffer
                if (!bufTemp.Append(szData, dwRead))
                {
                    // Critically low on memory
                    SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
                    goto Error;
                }

                // Get the accumulated string
                szTotal = bufTemp.GetData();
                cbTotal = bufTemp.GetLength();

                RILRetailTrace((TEXT("RILDrv : t : CComHandle::GetDTRDropResponse : Accumulated DTR response has %d chars: %s\r\n"), cbTotal,
                           TString(PrintableString(szTotal, cbTotal))));

                // Look for the indication of successful DTR drop
                if (MatchBytesAnywhere((BYTE*)szTotal, cbTotal, (BYTE*)"0\r\r\n", 4, pbRemainder))
                {
                    // We found "0\r\r\n", i.e. OK<CR> followed by <CR><LF> (starting an unsolicited notification)
                    //DEBUGCHK(NULL != pbRemainder);

                    // Include the terminating <CR><LF> in the remainder
                    pbRemainder -= 2;
                }
                else if (MatchBytesEnd((BYTE*)szTotal, cbTotal, (BYTE*)"0\r", 2, pbRemainder))
                {
                    // We found '0\r" at the very end of the data
                    //DEBUGCHK(NULL != pbRemainder);
                }

                if (pbRemainder)
                {
                    szRemainder = (LPCSTR)pbRemainder;
                    // Store any data before OK into the backup buffer
                    cbDataBeforeOK = szRemainder - szTotal - 2;
                    if (cbDataBeforeOK && !m_pBuffer->Append(szTotal, cbDataBeforeOK))
                    {
                        // Critically low on memory
                        SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
                        goto Error;
                    }

                    // Store any data after OK in the response buffer of the read thread
                    cbDataAfterOK = cbTotal - (szRemainder - szTotal);
                    if (cbDataAfterOK && !pRilDevice->AppendReadBytes(m_PortType, szRemainder, cbDataAfterOK))
                    {
                        goto Error;
                    }

                    RILRetailTrace((TEXT("RILDrv : i : CComHandle::GetDTRDropResponse : DTR successfuly dropped: %d bytes before, %d bytes after: %s\r\n"),
                               cbDataBeforeOK, cbDataAfterOK, TString(PrintableString(szRemainder,cbDataAfterOK))));

                    fRet = TRUE;
                    goto Error;
                }
            }
        }
    }
 
    Error:
    if (!fRet && bufTemp.GetLength())
    {
        // Append any data we've accumulated
        (void)m_pBuffer->Append(bufTemp.GetData(), bufTemp.GetLength());
        RILRetailTrace((TEXT("RILDrv : t : CComHandle::GetDTRDropResponse : Stored %d bytes in the buffer\r\n"), bufTemp.GetLength()));
    }
    return fRet;
    */
}
//
// Function passed to CQueue::ConditionalGet() below
//
BOOL FDialAnswerCommand(void* pItem, DWORD dwData)
{
    TBD_FUNCTION(FDialAnswerCommand);

    CCommand* pCmd = (CCommand*)pItem;
    return (pCmd->FDial() || pCmd->FAnswer());
}


//
//
//
DWORD WINAPI HangupThreadProc(LPVOID lpParameter)
{
    TBD_FUNCTION(HangupThreadProc);
    CCommand* pNextCmd = NULL;
    HANDLE hQuitEvent;
    CComHandle* pComDevice;
    CRilHandle* pRilDevice;
    COM_PORT_TYPE PortType;


    {
        HANGUP_THREAD_DATA* phtd = (HANGUP_THREAD_DATA*)lpParameter;

        hQuitEvent = phtd->hQuitEvent;
        pComDevice = phtd->pComDevice;
        pRilDevice = phtd->pRilDevice;
        PortType = phtd->PortType;
        delete phtd;
    }

    HANDLE hEvents[2] = {g_hNewDialOrHangupEvent, hQuitEvent};
    DWORD dwWait;
    for (;;)
    {
        dwWait = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
        if (dwWait != WAIT_OBJECT_0)
        {
            break;
        }

        if (SUCCEEDED(g_pCmdQ[PortType]->ConditionalGet(FDialAnswerCommand, 0, pNextCmd, 0)))
        {
            // The first command in the queue is a dial or answer
            // Because it hasn't been issued yet, we can abort it easily by
            // removing the command and faking a response.

#ifdef RIL_FAKECSQ
            g_fFakeSignalStrength = FALSE;
#endif
            // Fake a NOCARRIER response (acceptable for both failed dial and answer commands)
            pNextCmd->SendResponse(RIL_RESULT_NOCARRIER, NULL, 0);
            delete pNextCmd;
            pNextCmd = NULL;
        }
        else if (g_pCmdQ[PortType]->Peek(pNextCmd) && pNextCmd->FHangup())
        {
            DWORD dwTime;

            // The first command in the queue is a hangup, so the dial/answer has
            // already been issued.  We must abort the ongoing command.
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("HangupThreadProc : Aborting dial/answer"));
            pComDevice->SetCancelledDial();

            // Send AT/r to abort the call
            // Don't remove the command from the queue; the command thread will wake up later and
            // send an additional ATH\r in case the far end picked up before we got a chance to abort.
            // This abort must not be send MIN_TIME_BEFORE_ABORT after the command was send.
            dwTime = GetTickCount();
            if ((pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT > dwTime) && (pComDevice->GetLastCmdTime() < dwTime)) {
                Sleep(pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT - dwTime);
                RILRetailTrace((TEXT("RilDrv: Abort Command delayed by %dms\r\n"), pComDevice->GetLastCmdTime() + MIN_TIME_BEFORE_ABORT - dwTime));
            }
            (void)pComDevice->WriteCmdsToComPort(pRilDevice, "AT\r", 3);
            g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_SENDINGCMD, PrintableString("AT\r", 3));
#ifdef RIL_FAKECSQ
            g_fFakeSignalStrength = FALSE;
#endif
        }
    }

    (void)CloseHandle(hQuitEvent);
    return 0;
}

//
// Send an RIL command to the downstream port
//
BOOL CComHandle::SendRILCmdHandleRsp(CRilHandle* const pRilDevice, CCommand*& rpCmd, BOOL& rfHungUp, BOOL& rfTimedOut)
{
    // TBD_FUNCTION(CComHandle::SendRILCmdHandleRsp);

    LPSTR       szCmd;
    HRESULT     hr, hrPre;
    CResponse*  pRsp = NULL;
    CResponse*  pRspTmp = NULL;
    HANDLE      hQuitEvent = NULL;
    BOOL        fRet = FALSE;
    BOOL        fUnlocked = FALSE;
    PFN_PRE_CMD_CHECK   pfnPreCheck;

    rfHungUp = FALSE;
    rfTimedOut = FALSE;

    //TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("Executing command with ID: 0x%08x"), rpCmd->GetID());

    pfnPreCheck = rpCmd->GetPreCheckFunc();
    hrPre = (pfnPreCheck==NULL ? S_OK : pfnPreCheck(pRilDevice, m_PortType));
    if (rpCmd->FNoOp())
    {
        // This command is a no-op, so we can avoid sending it

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            goto Error;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        pRsp->MakeOK();

        // We can't possibly fail a no-op
        hr = S_OK;
    }
    else if (FAILED(hrPre))
    {
        // This command failed the pre-check so wie return an error.

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            goto Error;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        pRsp->MakeError(hrPre);

        // Fake an response from the module
        hr = S_OK;
    }
    else if (g_bRadioOff && !rpCmd->FInit() && !rpCmd->CmdOptIsSet(CMDOPT_IGNORERADIOOFF))
    {
        // The radio is off and the current command is not acceptable while the radio is in this state.

        // Allocate a new response
        pRsp = new CResponse(m_PortType, pRilDevice);
        if (!pRsp)
        {
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            goto Error;
        }

        // Create an error response to indicate that the radio is off.
        if (!pRsp->MakeError(RIL_E_RADIOOFF))
        {
            goto Error;
        }
    }
    else
    {
        m_fCommandOngoing = TRUE;

        // Here we're going to access the module, check if it is still alive before doing this
        if (!RHAFunctionList.pfnRHA_IsRadioPwrOn())
        {
            ExtRILRetailTrace(TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Module powered off externally, not sending: "), g_ComManager.GetDebugName(m_PortType), rpCmd->GetCmd());
            g_PowerManager.ForcePowerOff();
            hr = RIL_E_RADIOOFF;
            // Send out cancelled response as a notification
            rpCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
            goto Error;
        }

        // This command wasn't a no-op, so we need to send it
        szCmd = rpCmd->GetCmd();

        // Let RIL know we're waiting for AT response
        pRilDevice->StartWaitingForRsp(m_PortType, rpCmd);

        // We want the response that follows the command we are about to send.
        // Discard any responses that have accidentally queued up.
        while (SUCCEEDED(g_pRspQ[m_PortType]->Get(pRspTmp, 0)))
        {
            ExtRILRetailTrace(TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Discarding extra queued response: "), g_ComManager.GetDebugName(m_PortType), pRspTmp->GetData());
            delete pRspTmp;
            pRspTmp = NULL;
        }

        // Write the command out to the com port
        UINT iCmdLen = strlen(szCmd);
        if (!WriteCmdsToComPort(pRilDevice, szCmd, iCmdLen))
        {
            // Ignore the error. Even if we know that there was a problem writing to the com port,
            // we don't know what exactly was written nor whether we should expect a response.
            // It's best at this point to wait normally for a response. If no response is generated
            // due to the error, then we'll time out and continue as usual.
        }
        else
        {
            DWORD dwZone = rpCmd->CmdOptIsSet(CMDOPT_POLLING) ? RILLOG_ZONE_CMD_POLLING : RILLOG_ZONE_CMD;
            if (!rpCmd->CmdOptIsSet(CMDOPT_SUPPRESSLOGGING))
            {
                g_RilLog.LogEvent(dwZone, RILLOG_EVENT_SENDINGCMD, PrintableString(szCmd, iCmdLen));
            }
            else
            {
                g_RilLog.LogEvent(dwZone, RILLOG_EVENT_SENDINGCMDSUPPRESSED, rpCmd->GetAPIID());
            }
        }

        // If we haven't a second command part signal that the command has been sent
        if (!rpCmd->GetCmdPart2() && !fUnlocked)
        {
            fUnlocked = TRUE;
            UnlockRILCmd();
        }

        // If we've just sent a dial or answer command, prepare to handle a hangup command before getting a response
        if (rpCmd->FDial() || rpCmd->FAnswer())
        {
            // Launch a thread to wait for a hangup
            HANDLE hThread = NULL;
            hQuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
            if (hQuitEvent)
            {
                HANGUP_THREAD_DATA* phtd = new HANGUP_THREAD_DATA;
                if (phtd)
                {
                    (void)memset(phtd, 0x00, sizeof(HANGUP_THREAD_DATA));
                    phtd->hQuitEvent = hQuitEvent;
                    phtd->pComDevice = this;
                    phtd->pRilDevice = pRilDevice;
                    phtd->PortType = m_PortType;

                    hThread = CreateThread(NULL, 0, HangupThreadProc, (LPVOID)phtd, 0, NULL);
                }

                if (hThread)
                {
                    // We created the thread
                    // Don't need a handle to the thread...
                    CloseHandle(hThread);
                    // The thread takes care of freeing pthd and closing the event handle
                    // Therefore, don't touch phtd after this point
                }
                else
                {
                    // If we didn't create the thread, clean up and soldier on...
                    delete phtd;
                    phtd=NULL;
                    CloseHandle(hQuitEvent);
                    hQuitEvent=NULL;
                }
            }
        }

        // Get the response out of the Response Queue
        hr = g_pRspQ[m_PortType]->Get(pRsp, rpCmd->GetTimeout());

        if (SUCCEEDED(hr) && rpCmd->CmdOptIsSet(CMDOPT_INTERMEDIATERESPONSE) && (RIL_RESULT_OK == pRsp->GetNotifyCode()))
        {
            szCmd = rpCmd->GetCmdPart2();
            if (szCmd)
            {
                // If there is a secondary command, send it out now. This is primarily to handle SMS send commands
                // that need an intermediary prompt.
                pRilDevice->StopWaitingForRsp(m_PortType);
                pRilDevice->StartWaitingForRsp(m_PortType, rpCmd);
                iCmdLen = strlen(szCmd);
                if (!WriteCmdsToComPort(pRilDevice, szCmd, iCmdLen))
                {
                    // Ignore the error. Even if we know that there was a problem writing to the com port,
                    // we don't know what exactly was written nor whether we should expect a response.
                    // It's best at this point to wait normally for a response. If no response is generated
                    // due to the error, then we'll time out and continue as usual.
                }
                else
                {
                    DWORD dwZone = rpCmd->CmdOptIsSet(CMDOPT_POLLING) ? RILLOG_ZONE_CMD_POLLING : RILLOG_ZONE_CMD;
                    if (!rpCmd->CmdOptIsSet(CMDOPT_SUPPRESSLOGGING))
                    {
                        g_RilLog.LogEvent(dwZone, RILLOG_EVENT_SENDINGCMD, PrintableString(szCmd, iCmdLen));
                    }
                    else
                    {
                        g_RilLog.LogEvent(dwZone, RILLOG_EVENT_SENDINGCMDSUPPRESSED, rpCmd->GetAPIID());
                    }
                }

                // If it hasn't been done until now signal that the command has been sent
                if (!fUnlocked)
                {
                    fUnlocked = TRUE;
                    UnlockRILCmd();
                }

                // Now wait for the secondary response which will be the real deal.
                delete pRsp;
                pRsp = NULL;
                hr = g_pRspQ[m_PortType]->Get(pRsp, rpCmd->GetTimeout());
            }
        }

        // Check if we have been timed out before doing any other error handling
        // to properly handle possibly dead module
        if (RIL_E_TIMEDOUT == hr)
        {
            DWORD dwWritten;
            RILRetailTrace((TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Command has timed out!\r\n"), g_ComManager.GetDebugName(m_PortType)));
            ExtRILRetailTrace(TEXT("RilDrv: SendRILCmdHandleRsp - Cmd not sending: (%s) "), g_ComManager.GetDebugName(m_PortType), szCmd);

#ifdef RIL_LAST_ERROR
            g_dwLastError = MAKELONG(0, RADIO_TIMEOUTERROR);
#endif
            if (rpCmd->CmdOptIsSet(CMDOPT_SUPPRESSLOGGING))
            {
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_CMDTIMEDOUTSUPPRESSED, rpCmd->GetAPIID());
            }
            else
            {
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_CMDTIMEDOUT, PrintableString(szCmd, iCmdLen));
            }

            if (RHAFunctionList.pfnRHA_IsRadioPwrOn()) {
              INT iRetries;
              BOOL bATfailed;
              
              // Abort possibly still running command
              VirtWrite(ABORT_COMMAND, ABORT_COMMAND_SIZE, dwWritten);
              Sleep(1000);

              // Empty response queue
              while (SUCCEEDED(g_pRspQ[m_PortType]->Get(pRspTmp, 0)))
              {
                  delete pRspTmp;
                  pRspTmp = NULL;
              }

              iRetries = 0;
              bATfailed = TRUE;
              while (iRetries < MAX_AT_ATTEMPTS_AFTER_TIMEOUT)
              {
                  // Send an extra AT\r to check if the module is still alive
                  pRilDevice->StartWaitingForRsp(m_PortType, NULL);
                  WriteCharsToComPort(pRilDevice, "AT\r", 3);
                  g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_SENDINGCMD, PrintableString("AT\r", 3));
                  // Wait 1 second (+ 2 seconds to wake up module in +CFUN=7 mode) for the response
                  if (FAILED(g_pRspQ[m_PortType]->Get(pRspTmp, 3000)))
                  {
                    iRetries += 1;
                  }
                  else
                  {
                    bATfailed = FALSE;
                    break;
                  }
              }
              if (bATfailed)
              {
                  // We timed out again even with the simple AT -> the module must have been powered off
                  m_fCommandOngoing = FALSE;
                  pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
                  g_PowerManager.ForcePowerOff();
                  hr = RIL_E_RADIOOFF;
              }
              delete pRspTmp;
              pRspTmp = NULL;
            } else {
              // We have been powered off externally
              while (SUCCEEDED(g_pRspQ[m_PortType]->Get(pRspTmp, 0)))
              {
                  delete pRspTmp;
                  pRspTmp = NULL;
              }
              hr = RIL_E_RADIOOFF;
            }
        }

        // Let RIL know we're not waiting for AT response anymore
        pRilDevice->StopWaitingForRsp(m_PortType);

        // If we lauched a thread to wait for hangup, kill it
        if (hQuitEvent)
        {
            (void)SetEvent(hQuitEvent);
            hQuitEvent = NULL;
        }

        if (SUCCEEDED(hr))
        {
            // There is a remote chance that while waiting for a connect code
            // to go back into data mode, that we pick up some other rogue
            // response. We don't want RIL to go into data mode until we're
            // sure there is a connect. So, if we don't get a connect result,
            // try the command again.
            // If the command generates its own error response, then we will be
            // repeating this command/error for NUM_TIMEOUT_INIT_ATTEMPTS.
            // The last error response will be returned to the caller.
            if (rpCmd->FRequireConnectRsp() &&
                RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode())
            {
                RILRetailTrace((TEXT("RilDrv: SendRILCmdHandleRsp:  FRequireConnectRsp require send again. (FAILED)!\r\n")));
                goto Error;
                //continue;
            }

            // There are some cases where the SMS send command will fail
            // before RIL sends the PDU bytes. In this case, the PDU
            // bytes are bogus and an error response is returned from
            // the radio. The following code handles the extra error.
            // Note, however, that if the PDU is bad or if there is
            // problem sending the SMS on the network, then there is no
            // extra error to wait for, so RIL will needlessly block for
            // 5 seconds. This case should be rare enough that it is
            // worth the tradeoff.
            if ((APIID_SENDMSG == rpCmd->GetAPIID()) && (RIL_RESULT_ERROR == pRsp->GetNotifyCode()))
            {
                // Let RIL know we're waiting for AT response
                pRilDevice->StartWaitingForRsp(m_PortType, rpCmd);
                if (SUCCEEDED(g_pRspQ[m_PortType]->Get(pRspTmp, 5000)))
                {
                    delete pRspTmp;
                    pRspTmp = NULL;
                }
                // Let RIL know we're not waiting for AT response anymore
                pRilDevice->StopWaitingForRsp(m_PortType);
            }

            // If we cancelled the last dial command, turn the OK response into a NOCARRIER
            if (m_fCancelledDial)
            {
                if (rpCmd->FDial() || rpCmd->FAnswer())
                {
                    pRsp->SetCallAborted();
                }
                m_fCancelledDial = FALSE;
            }
            else if (rpCmd->FDial() && (RIL_RESULT_ERROR == pRsp->GetNotifyCode()) && rpCmd->CmdOptIsSet(CMDOPT_LONGDIALSTRING))
            {
                pRsp->MakeError(RIL_E_DIALSTRINGTOOLONG);
            }
        }
        else if (RIL_E_TIMEDOUT == hr)
        {
            // We timed out -> handle timeout and try if module is still alive
            RILRetailTrace((TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Command has timed out but module is still alive!\r\n"), g_ComManager.GetDebugName(m_PortType)));

            // Send a hint to the response thread that we could have bad data in the response buffer.
            pRilDevice->SetLastCommandTimedOut(m_PortType);
            // Additional operations may be required to cancel timed out dial commands, so we need to return a different error
            if (rpCmd->FDial())
            {
                hr = RIL_E_NORESPONSETODIAL;
            }

            // We fail timed out commands
            delete pRsp;
            pRsp = new CResponse(m_PortType, pRilDevice);
            if (!pRsp || !pRsp->MakeError(RIL_E_TIMEDOUT))
            {
                goto Error;
            }
        }
        // See if we couldn't get the response for some reason
        else if (RIL_E_RADIOOFF == hr)
        {
            // If the module has been switched off signal error to MMI and return
            RILRetailTrace((TEXT("RilDrv: SendRILCmdHandleRsp(%s) - Module has been switched off\r\n"), g_ComManager.GetDebugName(m_PortType)));
            // Send out cancelled response as a notification
            rpCmd->SendResponse(RIL_RESULT_ERROR, &hr, sizeof(HRESULT));
            goto Error;
        }
        else
        {
            goto Error;
        }

        // Did the command timed out internal?
        if (RIL_E_TIMEDOUT == pRsp->GetError())
        {
            rfTimedOut = TRUE;
            m_fCommandOngoing = FALSE;
        }
    }

    // If we issued a successful hangup command, exit out of the data mode
    if (rpCmd->FHangup() && ((RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode()) || (RIL_RESULT_OK == pRsp->GetNotifyCode())))
    {
        if (!g_fGPRSActive && !g_fReleaseHeldCall)
        {
            if (FDataMode())
        	{
	            ExitDataMode();
        	}
        }
    }

    // If we tried to get a connect response but failed,
    // then we've timed out.
    if (rpCmd->FRequireConnectRsp() &&
        (RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode() &&
         RIL_RESULT_NOCARRIER != pRsp->GetNotifyCode()))
    {
        rfTimedOut = TRUE;
    }

    // If the last command set the equipment state (+CFUN),
    // then update the global on/off state.
    if ((APIID_SETEQUIPMENTSTATE == rpCmd->GetAPIID()) && (RIL_RESULT_OK == pRsp->GetNotifyCode()))
    {
        g_bRadioOff = rpCmd->CmdOptIsSet(CMDOPT_SETRADIOON) ? FALSE : TRUE;

        // The radio is now on, so we'd better check to see if the SIM is ready
        if (!g_bRadioOff)
        {
            pRilDevice->StartReadyStateQuery();
        }
        else
        {
            // Radio's off -- reset g_dwReadyState
            pRilDevice->StopReadyStateQuery();
            pRilDevice->SendReadyStateNotification(RIL_READYSTATE_NONE, FALSE);
            UpdateSIMState(RIL_E_RADIOOFF);
        }
    }

    // If the last command resulted in error, then attempt
    // to retry the command. If no retry is necessary, then
    // RetryCommandOnError will just return without doing anything.
    if (RIL_RESULT_ERROR == pRsp->GetNotifyCode())
    {
        // It doesn't make sense to repeat the command when the error cause
        // is radio off, missing SIM card, invalid index or invalid dial string.
        if ((pRsp->GetError() != RIL_E_RADIOOFF)           &&
            (pRsp->GetError() != RIL_E_SIMNOTINSERTED)     &&
            (pRsp->GetError() != RIL_E_INVALIDINDEX)       &&
            (pRsp->GetError() != RIL_E_DIALSTRINGTOOLONG)  &&
            (pRsp->GetError() != RIL_E_INVALIDDIALSTRING))
        {
            if (rpCmd->RetryCommandOnError(m_PortType))
            {
                // A successful retry isn't an error but we must not call HandleRsp()
                // in this case and tidy up the command and response objects
                delete rpCmd;
                delete pRsp;
                fRet = TRUE;
                goto Error;
            }
        }
    }

    // Handle the response
    if (!pRilDevice->HandleRsp(m_PortType, rpCmd, pRsp, rfHungUp, g_bRadioOff))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    if (!fRet)
    {
        // If anything went wrong call the error callback to unblock possibly waiting functions
        if (rpCmd && rpCmd->GetErrorFunc())
        {
            rpCmd->GetErrorFunc()(NULL, rpCmd->GetParseParam());
        }
        delete rpCmd;
        delete pRsp;
    }
    if (hQuitEvent)
    {
        (void)CloseHandle(hQuitEvent);
    }

    m_fCommandOngoing = FALSE;

    // Make sure that the command handlig is unlocked again
    if (!fUnlocked)
    {
        UnlockRILCmd();
    }

    return fRet;
}

//
//
//
BOOL CComHandle::WriteCmdsToComPort(CRilHandle* const pRilDevice, LPCSTR szChars, UINT cbToWrite)
{
    LPCSTR szEnd = szChars+cbToWrite;

    for (;;)
    {
        // Look for a <cr>
        LPCSTR szEndCurr = strchr(szChars,'\r');
        // Write up to the <cr>, or up to end if no <cr> found
        cbToWrite = (szEndCurr) ? (szEndCurr - szChars + 1) : (szEnd - szChars);

        // Write command to com port
        if(!WriteCharsToComPort(pRilDevice,szChars,cbToWrite))
        {
            return FALSE;
        }

        // Move to beginning of next command
        szChars+=cbToWrite;

        // If we're done, exit
        if (szChars>=szEnd)
        {
            return TRUE;
        }

        // If not done, wait awhile before writing next command
        Sleep(100);
    }
}

//
//
//
BOOL CComHandle::WriteCharsToComPort(CRilHandle* const pRilDevice, LPCSTR szChars, UINT cbToWrite)
{
    // TBD_FUNCTION(CComHandle::WriteCharsToComPort);
    UINT cbOffset;
    DWORD dwWritten;
    LPSTR szBytes = NULL;
    BOOL fRet = FALSE;
    DWORD dwTimeout;

    RILRetailTrace((TEXT("RilDrv: ****************************************")));
    ExtRILRetailTrace(TEXT("RilDrv: Sending cmd (%s): "), g_ComManager.GetDebugName(m_PortType), szChars);
    RILRetailTrace((TEXT("RilDrv: ****************************************")));

    dwTimeout = 0;
    cbOffset = 0;
    while (cbToWrite)
    {
        if (!VirtWrite(szChars + cbOffset, cbToWrite, dwWritten))
        {
            goto Error;
        }
        cbToWrite -= dwWritten;
        cbOffset += dwWritten;
        if (!dwWritten)dwTimeout++;
        if(dwTimeout > 50)goto Error;
    }
    m_dwLastCmdSent = GetTickCount();
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        RILRetailTrace((TEXT("RilDrv: Failed to send cmd (%s)\r\n"), g_ComManager.GetDebugName(m_PortType)));
    }
    return fRet;
}


//
//
//
BOOL CComHandle::SetOwner(const CComInstanceHandle* const pNewOwner)
{
    // TBD_FUNCTION(CComHandle::SetOwner);
    SYNCBLOCK(m_csOwner);

    BOOL fRet = FALSE;

    if (m_pOwner)
    {
        // The port is already owned by someone -- can't comply
        goto Error;
    }
    m_pOwner = pNewOwner;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::ClearOwner(const CComInstanceHandle* const pOwner)
{
    // TBD_FUNCTION(CComHandle::ClearOwner);
    SYNCBLOCK(m_csOwner);

    BOOL fRet = FALSE;

    if (m_pOwner != pOwner)
    {
        // The port is owned by someone else
        goto Error;
    }
    m_pOwner = NULL;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
UINT CComHandle::ReadFromBackupBuffer(void* const pDestination, const DWORD cbToRead)
{
    // TBD_FUNCTION(CComHandle::ReadFromBackupBuffer);
    LPSTR szBuffer;
    UINT cbBuffer;
    UINT cbToCopy = 0;

    // If there's some data in the backup buffer, output it first
    if (m_pBuffer->GetLength())
    {
        // Get the data out of the backup buffer
        cbBuffer = m_pBuffer->GetLength();
        szBuffer = m_pBuffer->GiveUpData();

        if (cbToRead >= cbBuffer)
        {
            // We want to read more data than is in the buffer
            cbToCopy = cbBuffer;
        }
        else
        {
            // The buffer has more than we want to read
            cbToCopy = cbToRead;

            // Place the remainder back into the buffer
            (void)m_pBuffer->Append(szBuffer + cbToCopy, cbBuffer - cbToCopy);
        }

        // Copy the data to the output location
        memcpy(pDestination, szBuffer, cbToCopy);
        delete szBuffer;
    }
    return cbToCopy;
}


BOOL CComHandle::InitCommState(DCB* const pdcbOld) const
{
    BOOL fRet = FALSE;
    DCB dcb; memset(&dcb,0,sizeof(dcb)); // zero struct

    // Set communication parameters
    dcb.DCBlength = sizeof(DCB);

    fRet = GetCommState(m_hDownstream, &dcb);
    if (!fRet)
    {
        goto Error;
    }

    // Store the old values, if asked
    if (pdcbOld)
    {
        *pdcbOld = dcb;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("CComHandle::InitCommState : Initializing port at %d baud"), m_dwDownstreamBaudRate);

    dcb.BaudRate = m_dwDownstreamBaudRate;
    dcb.fBinary = TRUE;
    dcb.fParity = TRUE;
    dcb.fOutxCtsFlow = TRUE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fTXContinueOnXoff = TRUE;
    dcb.fOutX = FALSE;
    dcb.fInX = FALSE;
    dcb.fErrorChar = FALSE;
    dcb.fNull = FALSE;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;
    dcb.fAbortOnError = FALSE;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;

    fRet = SetCommState(m_hDownstream, &dcb);
    if (!fRet)
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    return fRet;
}

//
// Perform serial port initialization
//
BOOL CComHandle::InitComPortForRIL(DCB* const pdcbOld, COMMTIMEOUTS* const pctOld) const
{
    // TBD_FUNCTION(CComHandle::InitComPortForRIL);

    COMMTIMEOUTS ct; memset(&ct,0,sizeof(ct)); // zero struct
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }


    if (!InitCommState(pdcbOld))
    {
        goto Error;
    }

    // Set the DTR signal which is left cleared in InitCommState()
    if (!EscapeCommFunction(m_hDownstream, SETDTR))
    {
        goto Error;
    }

    // Set timeouts
    if (!GetCommTimeouts(m_hDownstream, &ct))
    {
        goto Error;
    }

    // Store old values, if asked
    if (pctOld)
    {
        *pctOld = ct;
    }


    ct.ReadIntervalTimeout          = MAXDWORD;
    ct.ReadTotalTimeoutMultiplier   = 0;
    ct.ReadTotalTimeoutConstant     = 0;
    ct.WriteTotalTimeoutMultiplier  = 0;
    ct.WriteTotalTimeoutConstant    = 3000;

    if (!SetCommTimeouts(m_hDownstream, &ct))
    {
        goto Error;
    }

    // Set the com mask to look for arriving characters
    if (!SetCommMask(m_hDownstream, DEFAULT_COM_MASK))
    {
        goto Error;
    }

    fRet = TRUE;

    Error:
    return fRet;
}


//
// Serial read
//
BOOL CComHandle::VirtRead(void* pBuf, const UINT cb, DWORD& rdwRead) const
{
    // TBD_FUNCTION(CComHandle::Read);
    DWORD dwDownstreamRead;
    BOOL fRet = FALSE;

    rdwRead = 0;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }

    if (cb > 0)
    {
        if (!DOWNST_READFILE(m_hDownstream, pBuf, cb, &dwDownstreamRead))
        {
            goto Error;
        }
        if (dwDownstreamRead)
        {
        /*
#ifdef TBD_DEBUG
            DWORD i;
            char szTmp[2001];

            gdwVirReadBytes += dwDownstreamRead;
            for (i=0; i<dwDownstreamRead && i<400; i++)
                sprintf(szTmp+(5*i), "0x%02x ",*((char*)pBuf+i));

            NKDbgPrintfW(L"gdwVirReadBytes: %d Virt Read %d Bytes: %a\r\n", gdwVirReadBytes, dwDownstreamRead, szTmp);
            
            //TBD_OUTPUT(TBDCT_READ, TBDOL_TRACE, TEXT("CComHandle::Read : %d Bytes: %a"), *((char*)pBuf+i));
#endif // TBD_DEBUG
        */
        }
        rdwRead = dwDownstreamRead;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Serial write
//
BOOL CComHandle::VirtWrite(const void* pBuf, const UINT cb, DWORD& rdwWritten) const
{
    // TBD_FUNCTION(CComHandle::Write);
    DWORD fRet = FALSE;
    DWORD dwDownstreamWritten;

    rdwWritten = 0;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }

    if (cb > 0)
    {
        if (!DOWNST_WRITEFILE(m_hDownstream, pBuf, cb, &dwDownstreamWritten))
        {
            goto Error;
        }
        if (m_fDataMode)
        {
            (DWORD)m_dwLastDataSent = GetTickCount();
        }
        rdwWritten = dwDownstreamWritten;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtWaitCommEvent(DWORD* const pdwMask) const
{
    // TBD_FUNCTION(CComHandle::VirtWaitCommEvent);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = DOWNST_WAITCOMMEVENT(m_hDownstream, pdwMask);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtClearCommBreak() const
{
    // TBD_FUNCTION(CComHandle::VirtClearCommBreak);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = ClearCommBreak(m_hDownstream);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtClearCommError(DWORD* const pdwErrors, COMSTAT* const pcs) const
{
    // TBD_FUNCTION(CComHandle::VirtClearCommError);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = ClearCommError(m_hDownstream, pdwErrors, pcs);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtEscapeCommFunction(const DWORD dwFunc) const
{
    // TBD_FUNCTION(CComHandle::VirtEscapeCommFunction);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = EscapeCommFunction(m_hDownstream, dwFunc);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommMask(DWORD* const pdwMask) const
{
    // TBD_FUNCTION(CComHandle::VirtGetCommMask);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = GetCommMask(m_hDownstream, pdwMask);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommMask(const DWORD dwMask) const
{
    TBD_FUNCTION(CComHandle::VirtSetCommMask);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = SetCommMask(m_hDownstream, dwMask);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommModemStatus(DWORD* const pdwStatus) const
{
    // TBD_FUNCTION(CComHandle::VirtGetCommModemStatus);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = GetCommModemStatus(m_hDownstream, pdwStatus);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommProperties(COMMPROP* const pcp) const
{
    // TBD_FUNCTION(CComHandle::VirtGetCommProperties);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = GetCommProperties(m_hDownstream, pcp);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommState(DCB* const pdcb) const
{
    // TBD_FUNCTION(CComHandle::VirtGetCommState);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = GetCommState(m_hDownstream, pdcb);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommState(DCB* const pdcb) const
{
    // TBD_FUNCTION(CComHandle::VirtSetCommState);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = SetCommState(m_hDownstream, pdcb);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtGetCommTimeouts(COMMTIMEOUTS* const pct) const
{
    // TBD_FUNCTION(CComHandle::VirtGetCommTimeouts);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = GetCommTimeouts(m_hDownstream, pct);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommTimeouts(COMMTIMEOUTS* const pct) const
{
    // TBD_FUNCTION(CComHandle::VirtSetCommTimeouts);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = SetCommTimeouts(m_hDownstream, pct);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtPurgeComm(const DWORD dwFlags) const
{
    // TBD_FUNCTION(CComHandle::VirtPurgeComm);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = PurgeComm(m_hDownstream, dwFlags);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtSetCommBreak() const
{
    // TBD_FUNCTION(CComHandle::VirtSetCommBreak);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = SetCommBreak(m_hDownstream);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtSetupComm(const DWORD dwInQueue, const DWORD dwOutQueue) const
{
    // TBD_FUNCTION(CComHandle::VirtSetupComm);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = SetupComm(m_hDownstream, dwInQueue, dwOutQueue);

    Error:
    return fRet;
}


//
//
//
BOOL CComHandle::VirtTransmitCommChar(const char cChar) const
{
    // TBD_FUNCTION(CComHandle::VirtTransmitCommChar);
    BOOL fRet = FALSE;

    if (!m_fInited || (INVALID_HANDLE_VALUE == m_hDownstream))
    {
        goto Error;
    }
    fRet = TransmitCommChar(m_hDownstream, cChar);

    Error:
    return fRet;
}



//
// Virtual serial instance handle ctor
//
CComInstanceHandle::CComInstanceHandle()
: m_pDevice (NULL),
m_fRsaMode(FALSE)
{
    //TBD_FUNCTION(CComInstanceHandle::CComInstanceHandle);
}


//
// Virtual serial instance handle dtor
//
CComInstanceHandle::~CComInstanceHandle()
{
    //TBD_FUNCTION(CComInstanceHandle::~CComInstanceHandle);

    // If this is the owner of the VSP, make sure it doesn't own the VSP anymore
    (void)m_pDevice->ClearOwner(this);
}


//
// Virtual serial instance handle initialization
//
BOOL CComInstanceHandle::Init(CComHandle* const pDevice, const DWORD dwAccess)
{
    //TBD_FUNCTION(CComInstanceHandle::Init);

    BOOL fRet = FALSE;

    m_pDevice = pDevice;

#ifdef SAP_SUPPORTED
    // Don't allow VSP to be opened if someone has already opened it
    if (!m_pDevice->SetOwner(this))
    {
        goto Error;
    }
    // If we're not in data mode assume that the port is used for RSA
    if (!m_pDevice->FDataMode())
    {
        m_fRsaMode = TRUE;
    }
#else // SAP_SUPPORTED
    // Don't allow VSP to be opened if we're not in data mode or if someone has already opened it
    if (!m_pDevice->FDataMode() || !m_pDevice->SetOwner(this))
    {
        goto Error;
    }
#endif // SAP_SUPPORTED

    fRet = TRUE;

    Error:
    return fRet;
}


#ifndef UNDER_CE
//
// Perform overlapped I/O operation (WinNT only)
//
BOOL PerformOverlappedIO(const OVERLAPPED_OPER oper, const HANDLE hCom, void* pbData, UINT cbData, DWORD* pdwData)
{
    TBD_FUNCTION(PerformOverlappedIO);

    // WinNT will only allow one thread at a time toi communicate to a COM port, so
    //    we must use asynchronous IO operations

    OVERLAPPED ol; memset(&ol,0,sizeof(ol)); // zero struct
    DWORD dwBytes;
    BOOL fRet = FALSE;

    ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!ol.hEvent)
    {
        goto Error;
    }

    switch (oper)
    {
    case OVERLAPPED_WAIT:
        fRet = WaitCommEvent(hCom, pdwData, &ol);
        break;

    case OVERLAPPED_READ:
        fRet = ReadFile(hCom, pbData, cbData, pdwData, &ol);
        break;

    case OVERLAPPED_WRITE:
        fRet = WriteFile(hCom, pbData, cbData, pdwData, &ol);
        break;
    }

    if (fRet)
    {
        // Operation completed synchronously
        goto Error;
    }
    else if (ERROR_IO_PENDING != GetLastError())
    {
        // Operation failed
        goto Error;
    }

    // Operation is overlapped -- wait for it to complete
    if (!GetOverlappedResult(hCom, &ol, &dwBytes, TRUE))
    {
        goto Error;
    }

    if (oper != OVERLAPPED_WAIT)
    {
        *pdwData = dwBytes;
    }
    fRet = TRUE;

    Error:
    if (ol.hEvent)
    {
        (void)CloseHandle(ol.hEvent);
    }
    return fRet;
}
#endif // UNDER_CE

